#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "test_utilities.h"
#include "../set.h"

/**
 * We're going to be doing this a lot, so we define a macro for this specific
 * test. You can define many such macros for repetitive assertions in your
 * unit-tests.
 */
#define ASSERT_SAME_PERSON(a,b) ASSERT_TEST(personCompare(a,b) == 0)

/** A simple datatype of Person to be used in testing the set
 * Note that this is not an ADT because the struct is exposed.
 **/
typedef struct Person_t {
	char* name;
	int id;
} *Person;

static Person personCreate(const char* name, int id);
static Person personCopy(Person person);
static void personDestroy(Person person);
static int personCompare(Person a, Person b);
static char* stringDuplicate(const char* str);

static char* stringDuplicate(const char* str) {
	assert(str);
	char* result = malloc(strlen(str)+1);
	return result ? strcpy(result,str) : NULL;
}

static void personDestroy(Person person) {
	if (!person) {
		return;
	}
	free(person->name);
	free(person);
}

static Person personCreate(const char* name, int id) {
	assert(name);
	Person person = malloc(sizeof(struct Person_t));
	if (!person) {
		return NULL;
	}
	person->name = stringDuplicate(name);
	if (!name) {
		personDestroy(person);
		return NULL;
	}
	person->id = id;
	return person;
}

static Person personCopy(Person person) {
	assert(person);
	return personCreate(person->name, person->id);
}

static int personCompare(Person a, Person b) {
	assert(a && b);
	int namesDifference = strcmp(a->name, b->name);
	if (namesDifference != 0) {
		return namesDifference;
	}
	return a->id - b->id;
}

/**
 * Helper functions for storing Person objects in a set
 */
static SetElement personCopyHelper(SetElement element) {
	return personCopy(element);
}

static void personDestroyHelper(SetElement element) {
	personDestroy(element);
}

static int personCompareHelper(SetElement element1, SetElement element2) {
	return personCompare(element1, element2);
}


/**
 * This struct is used to hold a set of examples
 * This allows us to save code on reinitializing big examples in the start of
 * each test
 */
typedef struct {
	Person haimMoshe;
	Person oferLevi;
	Person eyalGolan;
	Person aviBitter;
	Person idanTal;
	Set set;
} Examples;

/**
 * This function is called by the SET_UP macro in testing_utilities. This
 * function should initialize an examples struct that can be used for testing
 * the ADT
 * @return a new fully initialize examples sturct
 */
static Examples setUp() {
	Examples examples;
	examples.haimMoshe = personCreate("Haim Moshe", 111111111);
	examples.oferLevi = personCreate("Ofer Levi", 222222222);
	examples.eyalGolan = personCreate("Eyal Golan", 333333333);
	examples.aviBitter = personCreate("Avi Bitter", 444444444);
	examples.idanTal = personCreate("Idan Tal", 555555555);
	examples.set = setCreate(personCopyHelper, personDestroyHelper, 
	                         personCompareHelper);
	setAdd(examples.set, examples.eyalGolan);
	setAdd(examples.set, examples.oferLevi);
	setAdd(examples.set, examples.aviBitter);
	setAdd(examples.set, examples.haimMoshe);
	return examples;
}

/**
 * Called by the TEAR_DOWN macro from testing_utilities.h. This function should
 * deallocate and clear all the code
 * @param examples
 */
void tearDown(Examples examples) {
	personDestroy(examples.haimMoshe);
	personDestroy(examples.oferLevi);
	personDestroy(examples.eyalGolan);
	personDestroy(examples.aviBitter);
	personDestroy(examples.idanTal);
	setDestroy(examples.set);
}

/**
 * Set of tests, one for each function is usually a good thumb rule.
 */
static bool testSetCreate() {
	ASSERT_TEST(setCreate(NULL, NULL, NULL) == NULL);
	ASSERT_TEST(setCreate(NULL, NULL, NULL) == NULL);
	ASSERT_TEST(setCreate(personCopyHelper, NULL, NULL) == NULL);
	ASSERT_TEST(setCreate(NULL, personDestroyHelper, NULL) == NULL);
	ASSERT_TEST(setCreate(NULL, NULL, personCompareHelper) == NULL);
	ASSERT_TEST(setCreate(NULL, personDestroyHelper, personCompareHelper) == NULL);
	ASSERT_TEST(setCreate(personCopyHelper, NULL, personCompareHelper) == NULL);
	ASSERT_TEST(setCreate(personCopyHelper, personDestroyHelper, NULL) == NULL);
	Set set = setCreate(personCopyHelper, personDestroyHelper, personCompareHelper);
	ASSERT_TEST(set != NULL);
	setDestroy(set);

	return true;
}

static bool testSetCopy() {
	SET_UP(Examples);
	ASSERT_TEST(setCopy(NULL) == NULL);
	Set set = setCreate(personCopyHelper, personDestroyHelper, personCompareHelper);
	Set copy = setCopy(set);
	ASSERT_TEST(copy != NULL);
	ASSERT_TEST(0 == setGetSize(copy));
	setAdd(set, examples.aviBitter);
	ASSERT_TEST(0 == setGetSize(copy));

	Set copy2 = setCopy(examples.set);
	ASSERT_TEST(4 == setGetSize(copy2));
	ASSERT_SAME_PERSON(examples.aviBitter, setGetFirst(copy2));
	ASSERT_SAME_PERSON(examples.eyalGolan, setGetNext(copy2));
	ASSERT_SAME_PERSON(examples.haimMoshe, setGetNext(copy2));
	ASSERT_SAME_PERSON(examples.oferLevi, setGetNext(copy2));
	ASSERT_TEST(setGetNext(copy2) == NULL);

	setDestroy(set);
	setDestroy(copy);
	setDestroy(copy2);
	TEAR_DOWN();

	return true;
}

static bool testSetGetSize() {
	SET_UP(Examples);
	Set set = setCreate(personCopyHelper, personDestroyHelper, personCompareHelper);
	ASSERT_TEST(0 == setGetSize(set));
	setAdd(set, examples.aviBitter);
	ASSERT_TEST(1 == setGetSize(set));
	setAdd(set, examples.eyalGolan);
	ASSERT_TEST(2 == setGetSize(set));
	setRemove(set, examples.eyalGolan);
	ASSERT_TEST(1 == setGetSize(set));
	setDestroy(set);
	TEAR_DOWN();
	return true;
}

static bool testSetGetFirst() {
	SET_UP(Examples);
	Set set = setCreate(personCopyHelper, personDestroyHelper, personCompareHelper);
	ASSERT_TEST(setGetFirst(set) == NULL);
	Person first = setGetFirst(examples.set);
	ASSERT_SAME_PERSON(examples.aviBitter, first);
	ASSERT_SAME_PERSON(first, setGetFirst(examples.set));
	setDestroy(set);
	TEAR_DOWN();
	return true;
}

static bool testSetIsIn() {
	SET_UP(Examples);
	ASSERT_TEST(setIsIn(NULL, NULL) == false);
	ASSERT_TEST(setIsIn(NULL, examples.aviBitter) == false);
	ASSERT_TEST(setIsIn(examples.set, examples.aviBitter));
    setRemove(examples.set, examples.aviBitter);
	ASSERT_TEST(setIsIn(examples.set, examples.aviBitter) == false);
	TEAR_DOWN();
	return true;
}

static bool testSetAdd() {
	SET_UP(Examples);
	ASSERT_TEST(SET_NULL_ARGUMENT == setAdd(NULL, NULL));
	ASSERT_TEST(SET_NULL_ARGUMENT == setAdd(NULL, examples.aviBitter));
	ASSERT_TEST(SET_ITEM_ALREADY_EXISTS == setAdd(examples.set, examples.aviBitter));
	ASSERT_TEST(SET_SUCCESS == setRemove(examples.set, examples.aviBitter));
	ASSERT_TEST(SET_SUCCESS == setAdd(examples.set, examples.aviBitter));
	ASSERT_TEST(SET_SUCCESS == setAdd(examples.set, examples.idanTal));
	ASSERT_TEST(SET_ITEM_ALREADY_EXISTS == setAdd(examples.set, examples.idanTal));

	TEAR_DOWN();
	return true;
}

static bool testSetRemove() {
	SET_UP(Examples);
	ASSERT_TEST(SET_NULL_ARGUMENT == setRemove(NULL, NULL));
	ASSERT_TEST(SET_NULL_ARGUMENT == setRemove(NULL, examples.aviBitter));
	ASSERT_TEST(SET_SUCCESS == setRemove(examples.set, examples.aviBitter));
	ASSERT_TEST(SET_ITEM_DOES_NOT_EXIST == setRemove(examples.set, examples.aviBitter));
	ASSERT_TEST(SET_ITEM_DOES_NOT_EXIST == setRemove(examples.set, examples.idanTal));

	TEAR_DOWN();
	return true;
}


static bool testSetClear() {
	SET_UP(Examples);
	ASSERT_TEST(setClear(examples.set) == SET_SUCCESS);
	ASSERT_TEST(0 == setGetSize(examples.set));
	Set set = setCreate(personCopyHelper, personDestroyHelper, personCompareHelper);
	setClear(set);
	ASSERT_TEST(0 == setGetSize(set));
	setDestroy(set);
	TEAR_DOWN();
	return true;
}

static bool testSetDestroy() {
	setDestroy(NULL);
	return true;
}

int main() {
	RUN_TEST(testSetCreate);
	RUN_TEST(testSetCopy);
	RUN_TEST(testSetGetSize);
	RUN_TEST(testSetGetFirst);
	RUN_TEST(testSetAdd);
	RUN_TEST(testSetRemove);
	RUN_TEST(testSetIsIn);
	RUN_TEST(testSetClear);
	RUN_TEST(testSetDestroy);
	return 0;
}
